#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define TRUE	1
#define FALSE	0

char low_charset[] = "abcdefghijklmnopqrstuvwxyz";
char upp_charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char num_charset[] = "0123456789";
char spe_charset[] = "#&<>=+-_*%$!?,.:/^";
const unsigned long long JOB_SIZE = 1000000;

char *charset[15];
unsigned int inc[128];

char *memPartagee;


/***** SPLITING *****\
\* **************** */
unsigned long long nbElement(int *charsetNumber)
{
	int i;
	unsigned long long res=1;

	for(i=0; i<15; i++){
		switch(charsetNumber[i]){
			case -1:
				return res;
			case 0:
				res *= strlen(low_charset);
				break;
			case 1:
				res *= strlen(upp_charset);
				break;
			case 2:
				res *= strlen(num_charset);
				break;
			case 3:
				res *= strlen(spe_charset);
				break;
		}
	}
	return res;
}

unsigned long long wordToIndice(int *charsetNumber, char *word){
	int i,len=0;
	unsigned long long res=0;

	for(i=14; i>=0; i--){
		switch(charsetNumber[i]){
			case -1:
				len++;
				break;
			case 0:
				res += (word[i]-97)*pow(strlen(low_charset),(14-len)-i);
				break;
			case 1:
				res += (word[i]-65)*pow(strlen(upp_charset),(14-len)-i);
				break;
			case 2:
				res += (word[i]-48)*pow(strlen(num_charset),(14-len)-i);
				break;
			case 3:
				res += word[i]*pow(strlen(spe_charset),(14-len)-i);
				break;
		}
	}
	return res;
}

char* indiceToword(char *word, int *charsetNumber, unsigned long long number){//FAIL si masque > 12
	int indice,i,len=0;
	double powned;

	for(i=14; i>=0; i--){
		switch(charsetNumber[i]){
			case -1: 
				len++;
				break;
			case 0:
				powned = pow(strlen(low_charset),i);
				indice = (number/powned);
				word[(14-len)-i] = low_charset[indice];
				number -= (indice)*pow(strlen(low_charset),i);
				break;
			case 1:
				powned = pow(strlen(upp_charset),i);
				indice = (number/powned);
				word[(14-len)-i] = upp_charset[indice];
				number -= indice*pow(strlen(upp_charset),i);
				break;
			case 2:
				powned = pow(strlen(num_charset),i);
				indice = (number/powned);
				word[(14-len)-i] = num_charset[indice];
				number -= indice*pow(strlen(num_charset),i);
				break;
			case 3:
				powned = pow(strlen(spe_charset),i);
				indice = (number/powned);
				word[(14-len)-i] = spe_charset[indice];
				number -= indice*pow(strlen(spe_charset),i);
				break;
		}
	}
	word[15-len]='\0';
	return word;
}



/**** PROCESSING ****\
\* **************** */
int finished(char *block, int *charsetNumber, unsigned long long stop)
{
	unsigned int i;
	char stopblock[15];

	indiceToword(stopblock,charsetNumber,stop); 
	if(stop != 0 && strcmp(block,stopblock)==0){
		return TRUE;
	}
	for(i=0;i<strlen(block);i++)
		if(inc[i] < strlen(charset[i])-1)	
			return FALSE;

	return TRUE;
}

void increment(char *block, int len)
{
	int i;

	for(i=strlen(block)-1;i>-1;i--){
		if(inc[i] < strlen(charset[i])-1){
			inc[i]++;
			block[i] = charset[i][inc[i]];
			i=-1;
		} else {
			block[i] = charset[i][0];
			inc[i] = 0;
		}
	}
}

void chunk(int pwMin, int pwMax, int *charsetNumber, unsigned long long start, unsigned long long stop)
{
	int i,j,k,t;
	char block[16], startblock[16];
	unsigned long long shift = 0;
	memPartagee = malloc(16*sizeof(char)*JOB_SIZE);

	for(i=0; i<pwMax; i++)
	{
		switch(charsetNumber[i]){
			case 0:
				charset[i] = low_charset;
				break;
			case 1:
				charset[i] = upp_charset;
				break;
			case 2:
				charset[i] = num_charset;
				break;
			case 3:
				charset[i] = spe_charset;
				break;
			default:
				break;
		}
	}

	if(pwMax-pwMin < 0) 
		return;

	for(i=pwMin;i<=pwMax;i++){ // inc taille du pass (inutile avec le masque)
		memset(block,0,sizeof(block));

		for(j=0;j<i;j++){ // inc lettre du bloc
			if(start == 0LL){
				block[j] = charset[j][0];
				inc[j] = 0;
			} else {
				indiceToword(startblock,charsetNumber,start);
				block[j] = startblock[j];

				for(k=0;k<strlen(charset[j]);k++)
					if(block[j]==charset[j][k]) 
						inc[j] = k;
			}
		}

		memcpy(memPartagee+shift,block,16);
		shift += 16;
		while(!finished(block, charsetNumber, stop))
		{
			increment(block,i);
			memcpy(memPartagee+shift,block,16);
			shift += 16;
		}
	}
}

void split(int pwMin, int pwMax, int *charsetNumber){
	unsigned long long nbElem, lastJobSize;
	int i, nbPacket, start, stop;
	
	nbElem =  nbElement(charsetNumber);
	nbPacket = nbElem/JOB_SIZE;
	lastJobSize = nbElem%JOB_SIZE;

	if(nbElem < JOB_SIZE)
		chunk(pwMin, pwMax, charsetNumber,0,0);

	for(i=0; i<nbPacket; i++){
		chunk(pwMin,pwMax,charsetNumber,i*JOB_SIZE,(i+1)*JOB_SIZE);
		fprintf(stderr,"Packet %i / %i\n",i+1,nbPacket);
	}
}

/******** MAIN ******\
\* **************** */
/*
int main(int argc, char *argv[])
{
	int i,j,flag,min,max,charsetNumber[15];
	char def_charset[256];
	unsigned long long start=0LL,stop=0LL; 
	unsigned long long temp;

	if(argc<3){
		printf("usage: %s <from-len> <to-len> [-m mask] [-s startblock] [-e endblock]\n",argv[0]);
		return 0;
	}

	memset(def_charset,0,sizeof(def_charset));
	memset(charsetNumber,-1,sizeof(charsetNumber));


	min = atoi(argv[1]);
	max = atoi(argv[2]);

	for(i=1;i<argc-1;i++){
		
		if(strncmp(argv[i],"-m",2)==0){
			j=0;
			while(argv[i+1][j] == '?'){
				switch(argv[i+1][j+1]){
					case 'l':
						charsetNumber[j/2] = 0;
						break;
					case 'u':
						charsetNumber[j/2] = 1;
						break;
					case 'd':
						charsetNumber[j/2] = 2;
						break;
					case 's':
						charsetNumber[j/2] = 3;
						break;
					default:
						charsetNumber[j/2] = -1;
						break;
				}
				j += 2;
			}
		}

		if(strncmp(argv[i],"-s",2)==0){
			start = (unsigned long long)atoi(argv[i+1]);
		}
		if(strncmp(argv[i],"-e",2)==0){
			stop = (unsigned long long)atoi(argv[i+1]);
		}

	}
	//indiceToword(tempC,charsetNumber,start);
	//printf("mot = %s\n",tempC);
	//chunk(min,max,charsetNumber,start,stop);
	//printf("Nb Elements = %llu\n",nbElement(charsetNumber)); 
	split(min,max,charsetNumber);
	
	//numberToword(tempC,charsetNumber,start);
	//temp = wordToNumber(charsetNumber,tempC);
	//printf("Start = %s == %llu\n", tempC,temp);

	//numberToword(tempC,charsetNumber,stop);
	//temp = wordToNumber(charsetNumber,tempC);
	//printf("Stop = %s == %llu\n", tempC,temp);
	
	return 0;
}

*/
